1 module hip.net.backend.websocket;
2 import hip.wasm;
3 import hip.api.net.hipnet;
4 public import hip.network;
5 
6 version(WebAssembly):
7 
8 alias WasmWebsocket = size_t;
9 extern(C) WasmWebsocket connectWebsocket(
10     JSStringType url,
11     JSDelegateType!(void delegate()) onConnected,
12     JSDelegateType!(void delegate()) onClose,
13     JSDelegateType!(void delegate(size_t id)) onFirstMessage
14 );
15 extern(C) void closeWebsocket(WasmWebsocket);
16 extern(C) void websocketSendData(WasmWebsocket, uint from, uint to, size_t length, ubyte* dataPtr);
17 extern(C) ubyte* websocketGetData(WasmWebsocket);
18 
19 
20 /**
21  * Specification:
22  *  - The first message received by a Hipreme Engine websocket must always be its own connection ID.
23  */
24 class WASMWebsocketNetwork : INetworkBackend
25 {
26     WasmWebsocket socket;
27     NetConnectStatus _status;
28     uint socketID = NetID.server; //That is same as invalid since a socket can't be the server.
29     uint connectedTo = NetID.server;
30     void delegate() onConnect;
31 
32 
33     ///Buffer used if programmer tried to send data before assigning to its socket id
34     private ubyte[][] scheduledDataSend;
35 
36     bool isHost() const { return false; }
37 
38     NetConnectStatus connect(NetIPAddress ip, void delegate() onConnect, size_t id = NetID.server)
39     {
40         import hip.util.string;
41         if(_status == NetConnectStatus.waiting)
42             return _status;
43         this.onConnect = onConnect;
44         connectedTo = id;
45         String s;
46         if(!ip.ip.startsWith("ws://"))
47             s = String("ws://", ip.ip);
48         else
49             s = String(ip.ip);
50 
51         if(ip.port != 0)
52             s = String(s, ":", ip.port, "/ws");
53 
54         socket = connectWebsocket(
55             JSString(s.toString).tupleof,
56             sendJSDelegate!(()
57             {
58                 this._status = NetConnectStatus.connected;
59             }).tupleof,
60             sendJSDelegate!(()
61             {
62                 this._status = NetConnectStatus.disconnected;
63             }).tupleof,
64             sendJSDelegate!((size_t id)
65             {
66                 import hip.console.log;
67                 socketID = id;
68                 this.onConnect();
69                 logln("Connection Established. Socket ID: ", id, " connected to ", connectedTo);
70                 foreach(data; scheduledDataSend)
71                     websocketSendData(socket, socketID, connectedTo, data.length, data.ptr);
72                 scheduledDataSend = null;
73             }).tupleof
74         );
75 
76         return _status = NetConnectStatus.waiting;
77     }
78 
79     void targetConnectionID(size_t id)
80     {
81         connectedTo = id;
82         if(socketID != NetID.server && connectedTo == socketID)
83         {
84             import hip.util.string;
85             throw new Exception(SmallString("Tried to connect socket ", connectedTo, " to its same ID.").toString);
86         }
87     }
88     size_t getConnectionSelfID() const { return socketID; }
89     size_t targetConnectionID() const { return connectedTo; }
90 
91     bool sendData(ubyte[] data)
92     {
93         if(socketID < NetID.start)
94             scheduledDataSend~= data;
95         else
96             websocketSendData(socket, socketID, connectedTo, data.length, data.ptr);
97         return true;
98     }
99 
100     uint getData(ref ubyte[] tempBuffer)
101     {
102         tempBuffer = getWasmArray(websocketGetData(socket));
103         return tempBuffer.length;
104     }
105 
106     void disconnect()
107     {
108         if(socket != 0)
109         {
110             closeWebsocket(socket);
111             socket = 0;
112         }
113     }
114 
115     void attemptReconnection()
116     {
117 
118     }
119 
120     NetConnectStatus status() const { return _status; }
121 }